home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / newsgate / rfc822.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-21  |  14.7 KB  |  632 lines

  1. /*
  2. **  Routines to read and write mail and news headers.  The code here
  3. **  is gross and complicated, and it would be nice if somebody rewrote
  4. **  it to be clean and simple, especially the CrackFrom routine.
  5. */
  6. #include "gate.h"
  7. #include <time.h>
  8. #if    defined(RCSID)
  9. static char RCS[] =
  10.     "$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/rfc822.c,v 1.11 91/07/22 10:26:51 rsalz Exp $";
  11. #endif    /* defined(RCSID) */
  12.  
  13. #if    defined(HAVE_TIMEB)
  14. #include <sys/timeb.h>
  15. #else
  16. struct timeb {
  17.     time_t        time;
  18.     unsigned short    millitm;
  19.     short        timezone;
  20.     short        dstflag;
  21. };
  22. #endif    /* defined(HAVE_TIMEB) */
  23.  
  24. extern time_t        time();
  25. extern char        *asctime();
  26. extern struct tm    *gmtime();
  27.  
  28. #define QUESTIONABLE(c) \
  29.     ((c) == '"' || (c) == '(' || (c) == ')' || (c) == '\\')
  30.  
  31. #define HDR_OTHER        -1
  32. #define HDR_END            FALSE
  33. #define HDR_APPROVED         1
  34. #define HDR_CONTROL         2
  35. #define HDR_DATE         3
  36. #define HDR_DISTRIBUTION     4
  37. #define HDR_EXPIRE         5
  38. #define HDR_FOLLOWTO         6
  39. #define HDR_FROM          7
  40. #define HDR_KEYWORDS         8
  41. #define HDR_MESSAGEID         9
  42. #define HDR_NEWSGROUP         10
  43. #define HDR_ORGANIZATION    11
  44. #define HDR_REFERENCES        12
  45. #define HDR_REPLYTO        13
  46. #define HDR_SENDER        14
  47. #define HDR_SUMMARY        15
  48. #define HDR_TITLE         16
  49.  
  50.  
  51. /*
  52. **  The list of headers we recognize; all others are stripped.
  53. */
  54. typedef struct _HTYPE {
  55.     char    *Name;
  56.     int        Type;
  57. } HTYPE;
  58.  
  59. STATIC HTYPE    HeaderList[] = {
  60.     {    "Approved:",        HDR_APPROVED        },
  61.     {    "Control:",        HDR_CONTROL        },
  62.     {    "Date:",        HDR_DATE        },
  63.     {    "Posted:",        HDR_DATE        },
  64.     {    "Distribution:",    HDR_DISTRIBUTION    },
  65.     {    "Expires:",        HDR_EXPIRE        },
  66.     {    "Followup-To:",        HDR_FOLLOWTO        },
  67.     {    "From:",        HDR_FROM        },
  68.     {    "Keywords:",        HDR_KEYWORDS        },
  69.     {    "Message-ID:",        HDR_MESSAGEID        },
  70.     {    "Newsgroups:",        HDR_NEWSGROUP        },
  71.     {    "Organization:",    HDR_ORGANIZATION    },
  72.     {    "In-Reply-To:",        HDR_REFERENCES        },
  73.     {    "References:",        HDR_REFERENCES        },
  74.     {    "Reply-To:",        HDR_REPLYTO        },
  75.     {    "Sender:",        HDR_SENDER        },
  76.     {    "Summary:",        HDR_SUMMARY        },
  77.     {    "Subject:",        HDR_TITLE        },
  78.     {    "Title:",        HDR_TITLE        },
  79. };
  80.  
  81.  
  82.  
  83. /*
  84. **  Getline is like fgets, but deals with continuation lines.  It also
  85. **  ensures that even if a line that is too long is received, the
  86. **  remainder of the line is thrown away instead of treated like a second
  87. **  line.
  88. */
  89. STATIC char *
  90. Getline(buf, len, fp)
  91.     char        *buf;
  92.     int            len;
  93.     FILE        *fp;
  94. {
  95.     register char    *cp;
  96.     register int    c;
  97.     register int    n;
  98.  
  99.     for (n = 0, cp = buf; n < len && (c = getc(fp)) != EOF && c != '\n'; )
  100.     if (!iscntrl(c) || c == '\b' || c == '\t') {
  101.         *cp++ = c;
  102.         n++;
  103.     }
  104.     if (c == EOF && cp == buf)
  105.     return NULL;
  106.     *cp = '\0';
  107.  
  108.     if (c != '\n')
  109.     /* Line too long - read part didn't fit into a newline */
  110.     while ((c = getc(fp)) != '\n' && c != EOF)
  111.         continue;
  112.     else if (cp == buf) {
  113.     /* Don't look for continuation of blank lines */
  114.     *cp++ = '\n';
  115.     *cp = '\0';
  116.     return buf;
  117.     }
  118.  
  119.     while ((c = getc(fp)) == ' ' || c == '\t') {
  120.     /* Continuation line. */
  121.     if ((n += 2) < len) {
  122.         *cp++ = '\n';
  123.         *cp++ = c;
  124.     }
  125.     while ((c = getc(fp)) != '\n' && c != EOF)
  126.         if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
  127.         *cp++ = c;
  128.     }
  129.     if (n >= len - 1)
  130.     cp = buf + len - 2;
  131.     *cp++ = '\n';
  132.     *cp = '\0';
  133.     if (c != EOF)
  134.     /* push back first char of next header */
  135.     (void)ungetc(c, fp);
  136.     return buf;
  137. }
  138.  
  139.  
  140. /*
  141. **  I guess this is basically strncasecmp
  142. */
  143. STATIC int
  144. prefix(full, pref)
  145.     register char    *full;
  146.     register char    *pref;
  147. {
  148.     register char    fc;
  149.     register char    pc;
  150.  
  151.     while ((pc = *pref++) != '\0') {
  152.     fc = *full++;
  153.     if (isupper(fc))
  154.         fc = tolower(fc);
  155.     if (isupper(pc))
  156.         pc = tolower(pc);
  157.     if (fc != pc)
  158.         return FALSE;
  159.     }
  160.     return TRUE;
  161. }
  162.  
  163.  
  164. STATIC int
  165. HeaderType(p)
  166.     register char    *p;
  167. {
  168.     static int        save = HDR_END;
  169.     register HTYPE    *hp;
  170.     char        *colon;
  171.     char        *space;
  172.  
  173.     /* some consistency checks (i.e. is this really a header line?) */
  174.     if (p == NULL || !isascii(*p) || *p == '\n')
  175.     return save = HDR_END;
  176.  
  177.     if (WHITE(*p))
  178.     /* Continuation line. */
  179.     return save;
  180.  
  181.     /* If we don't get a "<no space> <colon> <space>", it's not a header. */
  182.     if ((colon = IDX(p, ':')) == NULL
  183.      || ((space = IDX(p, ' ')) && space < colon))
  184.     return save = HDR_END;
  185.  
  186.     for (hp = HeaderList; hp < ENDOF(HeaderList); hp++)
  187.     if (prefix(p, hp->Name))
  188.         return save = hp->Type;
  189.     return save = HDR_OTHER;
  190. }
  191.  
  192.  
  193. /*
  194. **  Get the contents of the field of the header line, appending it, with a
  195. **  space delimeter if it's a continuation line.  If there is already
  196. **  something in the header storage, skip this header line and the
  197. **  continuations.
  198. */
  199. STATIC void
  200. getfield(src, dest, size)
  201.     register char    *src;
  202.     register char    *dest;
  203.     register int    size;
  204. {
  205.     static int        skip = FALSE;
  206.     register char    *p;
  207.  
  208.     if (src == NULL || dest == NULL)
  209.     return;
  210.  
  211.     if (WHITE(*src)) {
  212.     /* Continuation line.  If skipping or no room, ignore. */
  213.     if (skip || (size -= strlen(dest)) <= 0)
  214.         return;
  215.     /* Munch all but one whitespace, append it to header. */
  216.     while (*src && WHITE(*src))
  217.         src++;
  218.     *--src = ' ';
  219.     (void)strncat(dest, src, size - 1);
  220.     }
  221.     else {
  222.     skip = FALSE;
  223.     if (*dest) {
  224.         /* Already got a value, so mark this as one to skip. */
  225.         skip = TRUE;
  226.         return;
  227.     }
  228.     if ((src = IDX(src, ':')) == NULL)
  229.         /* Can't happen! */
  230.         return;
  231.     /* Skip colon, eat whitespace. */
  232.     for (src++; *src && WHITE(*src); )
  233.         src++;
  234.     (void)strncpy(dest, src, size - 1);
  235.     }
  236.  
  237.     /* Munch trailing whitespace. */
  238.     for (p = dest + strlen(dest); --p >= dest && (*p == '\n' || WHITE(*p)); )
  239.     continue;
  240.     p[1] = '\0';
  241. }
  242.  
  243.  
  244. STATIC time_t
  245. cgtdate(datestr)
  246.     char        *datestr;
  247. {
  248.     static time_t    lasttime;
  249.     static char        save[SM_SIZE];
  250.     char        junk[40];
  251.     char        month[40];
  252.     char        day[30];
  253.     char        tod[60];
  254.     char        year[50];
  255.     char        buf[SM_SIZE];
  256. #if    defined(USE_GETABSDATE)
  257.     extern time_t    getabsdate();
  258. #else
  259.     extern time_t    getdate();
  260. #endif    /* defined(USE_GETABSDATE) */
  261.  
  262.     if (save[0] && EQ(datestr, save))
  263.     return lasttime;
  264.  
  265. #if    defined(USE_GETABSDATE)
  266.     lasttime = getabsdate(datestr, (struct timeb *)NULL);
  267. #else
  268.     lasttime = getdate(datestr, (struct timeb *)NULL);
  269. #endif    /* defined(USE_GETABSDATE) */
  270.  
  271.     if (lasttime < 0 &&
  272.       sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
  273.     (void)sprintf(buf, "%s %s, %s %s", month, day, year, tod);
  274. #if    defined(USE_GETABSDATE)
  275.     lasttime = getabsdate(buf, (struct timeb *)NULL);
  276. #else
  277.     lasttime = getdate(buf, (struct timeb *)NULL);
  278. #endif    /* defined(USE_GETABSDATE) */
  279.     }
  280.     (void)strncpy(save, datestr, sizeof save);
  281.     return lasttime;
  282. }
  283.  
  284.  
  285. /*
  286. **  Print the date in ARPA format, not ctime(3) format.
  287. */
  288. STATIC void
  289. DoDate(fp, ud)
  290.     FILE        *fp;
  291.     register char    *ud;
  292. {
  293.     register char    *p;
  294.     register char    *q;
  295.     register int    i;
  296.     char        buff[SM_SIZE];
  297.  
  298.     q = buff;
  299.  
  300. #if    0
  301.     /* until every site installs the fix to getdate.y, the day
  302.      * of the week can cause time warps */
  303.     /* "Mon, " */
  304.     p = &ud[0]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ','; *q++ = ' ';
  305. #endif    /* 0 */
  306.  
  307.     /* "16" */
  308.     p = &ud[8];
  309.     if (*p == ' ')
  310.     p++;
  311.     else
  312.     *q++ = *p++;
  313.     *q++ = *p++; *q++ = ' ';
  314.  
  315.     /* "Sep " */
  316.     p = &ud[4]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
  317.  
  318.     /* "79 " */
  319.     p = &ud[22]; *q++ = *p++; *q++ = *p++; *q++ = ' ';
  320.  
  321.     /* "01:03:52" */
  322.     for (p = &ud[11], i = 8; i > 0; i--)
  323.     *q++ = *p++;
  324.  
  325.     /* " GMT" */
  326.     *q++ = ' '; *q++ = 'G'; *q++ = 'M'; *q++ = 'T'; *q = '\0';
  327.  
  328.     Fprintf(fp, "Date: %s\n", buff);
  329. }
  330.  
  331.  
  332. /*
  333. **  Crack an RFC822 from header field into address and fullname.  We do
  334. **  this to make sure we write things out in official form.  "Be liberal
  335. **  in what you accept, conservative in what you generate."  Anyhow, we
  336. **  read things into three buffers, one for all <...> text, one for all
  337. **  (...) text, and a third for stuff not in either.  Either the first or
  338. **  third buffer will be the real address, depending on whether there is
  339. **  anything in buffer two or not.
  340. */
  341. int
  342. CrackFrom(addr, name, p)
  343.     ADDRCHAR        *addr;
  344.     char        *name;
  345.     char        *p;
  346. {
  347.     register char    *adp;
  348.     register char    *bp;
  349.     register ADDRCHAR    *ap;
  350.     register ADDRCHAR    *cp;
  351.     register ADDRCHAR    CurrChar;
  352.     register int    comment;
  353.     register int    address;
  354.     register int    addrfound;
  355.     register int    QuoteNext;
  356.     register int    InQuotes;
  357.     ADDRCHAR        *xp;
  358.     ADDRCHAR        comm[LG_SIZE];
  359.     char        commbuf[LG_SIZE];
  360.     char        addrbuf[LG_SIZE];
  361.  
  362.     /* Just to make sure. */
  363.     *name = '\0';
  364.     *addr = '\0';
  365.  
  366.     if (p == NULL)
  367.     return FALSE;
  368.  
  369.     /* Eat leading white space. */
  370.     while (*p && isspace(*p))
  371.     p++;
  372.  
  373.     /* Set defaults.  Start with an allocated copy of a comment string. */
  374.     comm[0] = '\0';
  375.     commbuf[0] = '\0';
  376.     addrbuf[0] = '\0';
  377.     adp = addrbuf;
  378.     comment = 0;
  379.     addrfound = 0;
  380.     address = 0;
  381.     QuoteNext = 0;
  382.     InQuotes = 0;
  383.     for (; *p; p++) {
  384.     CurrChar = (*p & UNQUOTE_MASK) | QuoteNext;
  385.     QuoteNext = CurrChar == '\\' ? QUOTE_MASK : 0;
  386.     if (!comment && CurrChar == '"')
  387.         InQuotes = InQuotes ? 0 : QUOTE_MASK;
  388.     else
  389.         CurrChar |= InQuotes;
  390.     switch (CurrChar) {
  391.     case '(':
  392.         if (comment == 0) {
  393.         cp = commbuf;
  394.         *cp = '\0';
  395.         }
  396.         comment++;
  397.         break;
  398.     case ')':
  399.         if (comment > 0 && --comment == 0) {
  400.         *cp = '\0';
  401.         xp = comm;
  402.         if (*xp) {
  403.             while (*xp)
  404.             xp++;
  405.             *xp++ = ',';
  406.             *xp++ = ' ';
  407.         }
  408.         for (cp = &commbuf[1]; (*xp++ = *cp) != '\0'; cp++)
  409.             continue;
  410.         cp = NULL;
  411.         continue;
  412.         }
  413.         break;
  414.     case '<':
  415.         if (address)
  416.         return FALSE;    /* AWK! Abort! */
  417.         if (!comment) {
  418.         address++;
  419.         *adp = '\0';
  420.         adp = addr;
  421.         }
  422.         break;
  423.     case '>':
  424.         if (!comment && address) {
  425.         address--;
  426.         addrfound++;
  427.         *adp = '\0';
  428.         for (adp = addrbuf; *adp; )
  429.             adp++;
  430.         continue;
  431.         }
  432.         break;
  433.     }
  434.  
  435.     if (comment)
  436.         *cp++ = CurrChar;
  437.     else if (!address || CurrChar != '<')
  438.         *adp++ = CurrChar;
  439.     if (*p == '\0')
  440.         break;
  441.     }
  442.  
  443.     *adp++ = '\0';
  444.  
  445.     if (addrfound) {
  446.     for (ap = name, xp = addrbuf; (*ap++ = (*xp & UNQUOTE_MASK)) != 0; xp++)
  447.         continue;
  448.     }
  449.     else {
  450.     for (cp = addr, xp = addrbuf; (*cp++ = *xp) != 0; xp++)
  451.         continue;
  452.     *name = '\0';
  453.     }
  454.  
  455.     /* Just to be sure that we got the full name, we'll take all of
  456.      * the comments. */
  457.     if (*comm) {
  458.     ap = name;
  459.     if (*ap) {
  460.         while (*ap)
  461.         ap++;
  462.         *ap++ = ',';
  463.         *ap++ = ' ';
  464.     }
  465.     for (xp = comm; (*ap = (*xp++ & UNQUOTE_MASK)) != 0; ap++)
  466.         continue;
  467.     }
  468.     /* Copy back, skipping leading spaces and trailing spaces. */
  469.     for (ap = addr, cp = addr; isspace(*cp); cp++)
  470.     continue;
  471.     while ((*ap = *cp++) != 0)
  472.     ap++;
  473.     while (*addr && isspace(*--ap))
  474.     *ap = '\0';
  475.  
  476.     /* Since characters in (comments) are interpreted differently from
  477.      * those in "phrase" <address>, play it safe and remove all
  478.      * questionable characters. */
  479.     for (ap = name, bp = name; isspace(*bp) || QUESTIONABLE(*bp); bp++)
  480.     continue;
  481.     while ((*ap = *bp++) != '\0')
  482.     if (!QUESTIONABLE(*ap))
  483.         ap++;
  484.     while (*name && isspace(*--ap))
  485.         *ap = '\0';
  486.     return TRUE;
  487. }
  488.  
  489.  
  490. /*
  491. **  Write out an RFC822 header, paying no attention to line limits.
  492. **  Ideally, we should do continuations in here...
  493. */
  494. int
  495. rfc822write(hp, fp)
  496.     register HBUF    *hp;
  497.     register FILE    *fp;
  498. {
  499.     time_t        t;
  500.  
  501.     if (hp->path[0])
  502.     Fprintf(fp, "Path: %s\n", hp->path);
  503.     if (hp->from[0])
  504.     Fprintf(fp, "From: %s\n", hp->from);
  505.     if (hp->nbuf[0])
  506.     Fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
  507.     if (hp->title[0])
  508.     Fprintf(fp, "Subject: %s\n", hp->title);
  509.     if (hp->ident[0])
  510.     Fprintf(fp, "Message-ID: %s\n", hp->ident);
  511.     /* Get current time. This will be used resolve the timezone. */
  512.     if (hp->subdate[0] == '\0')
  513.     (void)time(&t);
  514.     else if ((t = cgtdate(hp->subdate)) < 0) {
  515.     Fprintf(fp, "X-Unparseable-Date: %s\n", hp->subdate);
  516.     (void)time(&t);
  517.     }
  518.     DoDate(fp, asctime(gmtime(&t)));
  519.     if (hp->expdate[0])
  520.     Fprintf(fp, "Expires: %s\n", hp->expdate);
  521.     if (hp->followid[0])
  522.     Fprintf(fp, "References: %s\n", hp->followid);
  523.     if (hp->ctlmsg[0])
  524.     Fprintf(fp, "Control: %s\n", hp->ctlmsg);
  525.     if (hp->sender[0])
  526.     Fprintf(fp, "Sender: %s\n", hp->sender);
  527.     if (hp->replyto[0])
  528.     Fprintf(fp, "Reply-To: %s\n", hp->replyto);
  529.     if (hp->followto[0])
  530.     Fprintf(fp, "Followup-To: %s\n", hp->followto);
  531.     if (hp->distribution[0])
  532.     Fprintf(fp, "Distribution: %s\n", hp->distribution);
  533.     if (hp->organization[0])
  534.     Fprintf(fp, "Organization: %s\n", hp->organization);
  535.     if (hp->keywords[0])
  536.     Fprintf(fp, "Keywords: %s\n", hp->keywords);
  537.     if (hp->summary[0])
  538.     Fprintf(fp, "Summary: %s\n", hp->summary);
  539.     if (hp->approved[0])
  540.     Fprintf(fp, "Approved: %s\n", hp->approved);
  541.     Fprintf(fp, "\n");
  542.     return !ferror(fp);
  543. }
  544.  
  545.  
  546. rfc822read(hp, fp, buff, buffsize)
  547.     register HBUF    *hp;
  548.     register FILE    *fp;
  549.     char        *buff;
  550.     int            buffsize;
  551. {
  552.     register int    i;
  553.     long        curpos;
  554.  
  555.     /* Zap out the headers. */
  556.     hp->approved[0] = '\0';
  557.     hp->ctlmsg[0] = '\0';
  558.     hp->subdate[0] = '\0';
  559.     hp->distribution[0] = '\0';
  560.     hp->expdate[0] = '\0';
  561.     hp->followto[0] = '\0';
  562.     hp->from[0] = '\0';
  563.     hp->followid[0] = '\0';
  564.     hp->keywords[0] = '\0';
  565.     hp->ident[0] = '\0';
  566.     hp->nbuf[0] = '\0';
  567.     hp->organization[0] = '\0';
  568.     hp->title[0] = '\0';
  569.     hp->replyto[0] = '\0';
  570.     hp->summary[0] = '\0';
  571.     hp->path[0] = '\0';
  572.     hp->sender[0] = '\0';
  573.  
  574.     i = HeaderType(buff);
  575.     do {
  576.     curpos = ftell(fp);
  577.     switch (i) {
  578.     case HDR_APPROVED:
  579.         getfield(buff, hp->approved, sizeof hp->approved);
  580.         break;
  581.     case HDR_CONTROL:
  582.         getfield(buff, hp->ctlmsg, sizeof hp->ctlmsg);
  583.         break;
  584.     case HDR_DATE:
  585.         getfield(buff, hp->subdate, sizeof hp->subdate);
  586.         break;
  587.     case HDR_DISTRIBUTION:
  588.         getfield(buff, hp->distribution, sizeof hp->distribution);
  589.         break;
  590.     case HDR_EXPIRE:
  591.         getfield(buff, hp->expdate, sizeof hp->expdate);
  592.         break;
  593.     case HDR_FOLLOWTO:
  594.         getfield(buff, hp->followto, sizeof hp->followto);
  595.         break;
  596.     case HDR_FROM:
  597.         getfield(buff, hp->from, sizeof hp->from);
  598.         break;
  599.     case HDR_KEYWORDS:
  600.         getfield(buff, hp->keywords, sizeof hp->keywords);
  601.         break;
  602.     case HDR_MESSAGEID:
  603.         getfield(buff, hp->ident, sizeof hp->ident);
  604.         break;
  605.     case HDR_NEWSGROUP:
  606.         getfield(buff, hp->nbuf, sizeof hp->nbuf);
  607.         break;
  608.     case HDR_ORGANIZATION:
  609.         getfield(buff, hp->organization, sizeof hp->organization);
  610.         break;
  611.     case HDR_REFERENCES:
  612.         getfield(buff, hp->followid, sizeof hp->followid);
  613.         break;
  614.     case HDR_REPLYTO:
  615.         getfield(buff, hp->replyto, sizeof hp->replyto);
  616.         break;
  617.     case HDR_SENDER:
  618.         getfield(buff, hp->sender, sizeof hp->sender);
  619.         break;
  620.     case HDR_SUMMARY:
  621.         getfield(buff, hp->summary, sizeof hp->summary);
  622.         break;
  623.     case HDR_TITLE:
  624.         getfield(buff, hp->title, sizeof hp->title);
  625.         break;
  626.     }
  627.     } while ((i = HeaderType(Getline(buff, buffsize, fp))) != HDR_END);
  628.  
  629.     if (*buff != '\n')
  630.     (void)fseek(fp, curpos, 0);
  631. }
  632.